use super::ffi::{
    osDelay, osKernelGetTickFreq, osPriority_t_osPriorityAboveNormal, osThreadAttr_t, osThreadNew,
};
use alloc::boxed::Box;
use alloc::string::String;
use core::ffi::{c_char, c_void};
use core::mem;
use core::ptr::null_mut;

pub fn sleep_ms(ms: u32) {
    unsafe { osDelay(ms * osKernelGetTickFreq() / 1000) };
}

pub struct JoinHandle {}

impl JoinHandle {
    pub(crate) fn new() -> Self {
        Self {}
    }
}

pub struct Builder {
    stack_size: usize,
    name: String,
}

impl Builder {
    pub fn new() -> Self {
        Self {
            stack_size: 2048,
            name: String::from("\0"),
        }
    }

    pub fn stack_size(mut self, size: usize) -> Self {
        self.stack_size = size;
        self
    }

    pub fn name(mut self, name: String) -> Self {
        self.name = name;
        self.name.push_str("\0");
        self
    }

    pub fn spawn<F>(self, f: F) -> crate::errors::Result<JoinHandle>
    where
        F: FnOnce(),
        F: Send + 'static,
    {
        unsafe {
            let mut attr: osThreadAttr_t = mem::zeroed();

            attr.name = self.name.as_ptr() as *const c_char;
            attr.attr_bits = 0;
            attr.cb_mem = null_mut();
            attr.cb_size = 0;
            attr.stack_mem = null_mut();
            attr.stack_size = self.stack_size as u32;
            attr.priority = osPriority_t_osPriorityAboveNormal;
            let func: Box<Box<dyn FnOnce()>> = Box::new(Box::new(f));
            let p = Box::into_raw(func);
            infoln!("thread args {:p}", p);
            if osThreadNew(Some(thread_entry), p as *mut c_void, &attr) == null_mut() {
                return Err(crate::errors::Error::Other(String::from(
                    "start ios sdk thread failed",
                )));
            }
            Ok(JoinHandle::new())
        }
    }
}

pub fn spawn<F>(f: F) -> crate::errors::Result<JoinHandle>
where
    F: FnOnce(),
    F: Send + 'static,
{
    Builder::new().spawn(f)
}

extern "C" fn thread_entry(args: *mut c_void) {
    infoln!("thread entry, args {:p}", args);
    if !args.is_null() {
        let func: Box<Box<dyn FnOnce() + 'static>> = unsafe { Box::from_raw(args as *mut _) };
        infoln!("calling func {:p}", &func);
        func();
    }
}
